﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Input;
using System.Windows.Media;

namespace UmlControlLibrary
{
    public interface IDraggable
    {
        void Drag(double deltaX, double deltaY);
    }

    public class SelectableItemControl:UserControl
    {
        public delegate void SelectDelegate(object sender);

        public static DependencyProperty IsSelectableProperty = DependencyProperty.Register("Selectable", typeof(bool), typeof(SelectableItemControl), new PropertyMetadata(true));
        public static int GridSize = 5;

        public bool Selectable
        {
            get { return (bool)GetValue(IsSelectableProperty); }
            set { SetValue(IsSelectableProperty, value); }
        }

        /// <summary>
        /// When control is moved by program, this should be set to true to avoid mouse move duplicate call
        /// </summary>
        private bool _ignoreNextMouseMove = false;
        public bool IgnoreNextMouseMove
        {
            get { return _ignoreNextMouseMove; }
            set { _ignoreNextMouseMove = value; }
        }

        public SelectableItemControl OwnerElement
        {
            get;
            set;
        }

        public SelectableItemControl SelectedItem
        {
            get;
            set;
        }

        public List<UIElement> DragControls
        {
            get;
            set;
        }

        public double Left
        {
            get;
            set;
        }

        public double Right
        {
            get;
            set;
        }

        public double Top
        {
            get;
            set;
        }

        public double Bottom
        {
            get;
            set;
        }

        public object GetTemplateElement(object sender, string elementName)
        {
            FrameworkElement c = sender as Control;
            FrameworkTemplate template;
            template = null;
            if (sender is ContentPresenter)
                template = (sender as ContentPresenter).ContentTemplate;
            else if (sender is Control)
                template = (sender as Control).Template;

            return template.FindName(elementName, c);
        }

        public virtual Control GetTemplatedElement()
        {           
            return (this.TemplatedParent ?? this) as Control;
        }

        public static double AlignToGrid(double d)
        {
            return (Math.Round(d / GridSize)) * GridSize;
        }

        virtual public void Moved()
        {
            Control element = this.GetTemplatedElement();
            Left = Canvas.GetLeft(element);
            Top = Canvas.GetTop(element);
            Right = element.ActualWidth + Left;
            Bottom = element.ActualHeight + Top;
            if (element is SelectableItemControl)
            {
                SelectableItemControl se = (SelectableItemControl)element;
                se.Left = Left;
                se.Right = Right;
                se.Top = Top;
                se.Bottom = Bottom;
            }

            Canvas.SetLeft(this, Left);
            Canvas.SetTop(this, Top);

            OwnerElement.ChildMoved(this);
        }

        virtual public void Resized()
        {
            double l = Canvas.GetLeft(this);
            Left = AlignToGrid(l);
            double t = Canvas.GetTop(this);
            Top = AlignToGrid(t);
            Right = AlignToGrid(this.ActualWidth + l);
            Bottom = AlignToGrid(this.ActualHeight + t);
            OwnerElement.ChildResized(this);
        }

        public void CalculateBounds()
        {
            Moved();
        }

        public bool Dragging = false;
        public Point LastMouseDownPos;
        public Point LastDragPos;

        private SelectableItemControl selectableParent = null;
        
        public SelectableItemControl SelectableParent
        {
            get {
                EnsureSelectableParent();
                return selectableParent;
            }
        }


        public SelectableItemControl()
        {
            this.DragControls = new List<UIElement>();
            this.DragControls.Add(this);
            this.OwnerElement = this;
            this.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(SelectableItemControl_MouseLeftButtonDown);
            this.MouseMove += new System.Windows.Input.MouseEventHandler(SelectableItemControl_MouseMove);
            this.PreviewMouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(SelectableItemControl_PreviewMouseLeftButtonUp);
            this.ContextMenuOpening += new ContextMenuEventHandler(ContextMenu_Opening);
        }

        private void ContextMenu_Opening(object sender, ContextMenuEventArgs e)
        {
            if (e.Source is FrameworkElement)
            {
                if ((e.Source as FrameworkElement).ContextMenu != null)
                {
                    (e.Source as FrameworkElement).ContextMenu.DataContext = this.DataContext;
                    //e.Handled = false;
                }
            }
        }
 
        public void AddDragZone(UIElement e)
        {
            if (DragControls.Contains(this))
                DragControls.Remove(this);
            DragControls.Add(e);
            if (e != null)
                e.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(DragZone_MouseLeftButtonDown);
        }

        void DragZone_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (Selectable)
            {
                if (OwnerElement.SelectedItem != null)
                    OwnerElement.SelectedItem.Dragging = false;
               // if (OwnerElement.SelectedItem != this)
                    Select();

                if ((this is IDraggable) && (DragControls.Contains(sender as UIElement)))
                {
                    Dragging = true;
                }
                OwnerElement.LastMouseDownPos = OwnerElement.GetMousePos(e);
                OwnerElement.LastDragPos = OwnerElement.LastMouseDownPos;
                e.Handled = true;
            }
        }
        
        virtual public Point GetMousePos(System.Windows.Input.MouseEventArgs e)
        {
            return new Point(0,0);
        }

        public SelectableItemControl(SelectableItemControl owner)
            : this()
        {
            this.OwnerElement = owner;
        }

        void SelectableItemControl_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (Selectable)
            {
                if (OwnerElement.SelectedItem != null)
                    OwnerElement.SelectedItem.Dragging = false;
            }
        }


        int count = 0;
        Point screenCoords = new Point(0, 0);
        void SelectableItemControl_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (Selectable)
            {
                if (OwnerElement.SelectedItem != null)
                {
                    if (e.LeftButton == System.Windows.Input.MouseButtonState.Released)
                        OwnerElement.SelectedItem.Dragging = false;

                    if (OwnerElement.SelectedItem.Dragging)//&& (!OwnerElement.SelectedItem.IgnoreNextMouseMove))
                    {
                        SelectableItemControl c = OwnerElement.SelectedItem as SelectableItemControl;
                        Point newPos = OwnerElement.GetMousePos(e);
                 //       EventService.Log(this, new Event("Drag", typeof(UmlDiagramControl)), newPos.ToString());
                        double deltaX, deltaY;
                        if (c == OwnerElement)
                        {
                            deltaX = newPos.X - OwnerElement.LastDragPos.X;
                            deltaY = newPos.Y - OwnerElement.LastDragPos.Y;
                        }
                        else
                        {
                            deltaX = AlignToGrid(newPos.X) - AlignToGrid(OwnerElement.LastDragPos.X);
                            deltaY = AlignToGrid(newPos.Y) - AlignToGrid(OwnerElement.LastDragPos.Y);
                        }
                        if ((deltaX != 0) || (deltaY != 0))
                        {
                            OwnerElement.LastDragPos = newPos;
                            (OwnerElement.SelectedItem as IDraggable).Drag(deltaX, deltaY);
                        }
                    }
                }
            }
        }

        void SelectableItemControl_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (Selectable)
            {
                //OwnerElement.SelectedItem = (sender as SelectableItemControl);
                if (OwnerElement.SelectedItem != null)
                    OwnerElement.SelectedItem.Dragging = false;
              //  if (OwnerElement.SelectedItem != this)
                    Select();
                /*     Dragging = true;
                     if (e.Source == OwnerElement)
                     {
                         LastMouseDownPos = OwnerElement.GetMousePos(e);
                         LastDragPos = LastMouseDownPos;
                     }*/
                //  e.Handled = true;
            }
        }

      /*  virtual public void Deselect(SelectableItemControl item)
        {
            if (SelectedItem == item)
            {
                SelectedItem.Deselect();
                SelectedItem = null;
            }
        }*/

        public SelectableItemControl GetSelectableParent()
        {
            object o;
            o = Parent;
            if (o == null)
                o = this.TemplatedParent;
            while (!((o==null) || (o is SelectableItemControl)))
                o = VisualTreeHelper.GetParent((DependencyObject)o);
            return o as SelectableItemControl;
        }

        private void EnsureSelectableParent()
        {
            if (selectableParent == null)
            {
                selectableParent = GetSelectableParent();
                if (selectableParent != null)
                    OwnerElement = selectableParent;//.OwnerElement;
                //        OwnerElement.OnDeselect += new SelectDelegate(OnOwnerDeselect);
            }
        }

        public event SelectDelegate OnDeselect;
        virtual public void Deselect()
        {
            /*EnsureSelectableParent();
            if ((OwnerElement != null) && (OwnerElement != this))
            {
                OwnerElement.Deselect(this);
                if (SelectedItem != null)
                    SelectedItem.Deselect();
                if (OnDeselect != null)
                    OnDeselect(this);
            }*/
            EnsureSelectableParent();
            if (SelectedItem != null)
            {
                SelectedItem.Deselect();
                SelectedItem = null;
            }
            if (OnDeselect != null)
                OnDeselect(this);
            
            /*if ((OwnerElement != null) && (OwnerElement != this))
            {
                OwnerElement.Deselect(this);
            }*/
        }

        virtual public void Select(SelectableItemControl item)
        {
            if (SelectedItem != item)
            {
                EnsureSelectableParent();
          /*      if ((SelectedItem != null) && (SelectedItem != this))
                {
                    SelectedItem.Deselect();
                }*/
               /* if (OwnerElement != this)
                    OwnerElement.Select(this);
                * */
                if (OwnerElement != this) 
                    Select();
                SelectedItem = item;
                if (OnSelect != null)
                    OnSelect(this);
            }
        }

        private bool IsSelected()
        {
            if (OwnerElement != null)
                return OwnerElement.SelectedItem == this;
            else
                return false;
        }

        private void DeselectSelected()
        {          
            if ((SelectedItem != null) && (SelectedItem != this))
                SelectedItem.Deselect();
        }

        public event SelectDelegate OnSelect;
        virtual public void Select()
        {
            EnsureSelectableParent();

            if (!IsSelected())
            {
                if (OwnerElement != null) //&& (OwnerElement != this))
                {
                    OwnerElement.DeselectSelected();
                    OwnerElement.Select(this);
                }
            }
        }

        public bool Selected
        {
            get { return OwnerElement.SelectedItem == this; }
        }

        public virtual void ChildMoved(SelectableItemControl child)
        {
        }

        public virtual void ChildResized(SelectableItemControl child)
        {
        }

/*        public virtual IAddChild ChildContainer
        {
            get;
            set;
        }
        */
       // public virtual I

        public virtual void RemoveFromParent()
        {
            OwnerElement.RemoveChild(this);
        }

        public virtual void AddChild(object child)
        {
            if (child is SelectableItemControl)
                ((SelectableItemControl)child).OwnerElement = this;
        }

        public virtual void RemoveChild(object child)
        { 
        }
    }
}
